/**
 * 
 */
package com.eagle.coders.xml.transformation;

import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.List;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

import com.eagle.coders.common.ui.form.renderer.cache.ComboboxDisplayBean;
import com.eagle.coders.xml.transformation.beans.Action;
import com.eagle.coders.xml.transformation.beans.Actions;
import com.eagle.coders.xml.transformation.beans.Binding;
import com.eagle.coders.xml.transformation.beans.Column;
import com.eagle.coders.xml.transformation.beans.ComboBox;
import com.eagle.coders.xml.transformation.beans.ComboDisplayComponent;
import com.eagle.coders.xml.transformation.beans.DomainGroup;
import com.eagle.coders.xml.transformation.beans.DomainGroups;
import com.eagle.coders.xml.transformation.beans.DomainProperties;
import com.eagle.coders.xml.transformation.beans.DomainProperty;
import com.eagle.coders.xml.transformation.beans.Domains;
import com.eagle.coders.xml.transformation.beans.NodeLabel;
import com.eagle.coders.xml.transformation.beans.NodeType;
import com.eagle.coders.xml.transformation.beans.NodeValidation;
import com.eagle.coders.xml.transformation.beans.Properties;
import com.eagle.coders.xml.transformation.beans.PropertyBinding;
import com.eagle.coders.xml.transformation.beans.Radio;
import com.eagle.coders.xml.transformation.beans.RadioButton;
import com.eagle.coders.xml.transformation.beans.Row;
import com.eagle.coders.xml.transformation.beans.Table;
import com.eagle.coders.xml.transformation.beans.TableColumn;
import com.eagle.coders.xml.transformation.beans.Usecase;
import com.eagle.coders.xml.transformation.beans.UsecaseResourceBundle;
import com.eagle.coders.xml.transformation.beans.Widget;
import com.eagle.coders.xml.transformation.display.beans.DisplayingPolicies;
import com.eagle.coders.xml.transformation.display.beans.DisplayingPolicy;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;

/**
 * @author Anees
 * 
 */
public class Transformer {
	
	public Transformer(){
		
	}

	public Usecase toUsecase(String xml, String usecaseId) {
		try {
			
			Usecase usecase = new Usecase();
			
			SAXReader reader = new SAXReader();
			byte[] bytes = xml.getBytes();
			ByteArrayInputStream iStream = new ByteArrayInputStream(bytes);
			Document doc = reader.read(iStream);
			Element usecaseNode = (Element) doc.selectSingleNode("usecases/usecase[@id='" + usecaseId+ "']");
			
			Actions actions = processAction(usecaseNode);
			
			DisplayingPolicies displayingPolicies = processDisplayPolicies(usecaseNode);
			List<Domains> domainList = processDomains(usecaseNode);
			
			UsecaseResourceBundle usecaseResourceBundle = processResourceBundle(usecaseNode);
			
			usecase.setUsecaseResourceBundle(usecaseResourceBundle);
			usecase.setDomainList(domainList);
			usecase.setDisplayingPolicies(displayingPolicies);
			usecase.setActions(actions);
			usecase.setId(usecaseId);
			
			return usecase;
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}
	
	public static void displayFormLayouting(DisplayingPolicies displayingPolicies){
		
		
	}

	/**
	 * 
	 * @param usecaseNode
	 * @return
	 */
	private UsecaseResourceBundle processResourceBundle(Node usecaseNode){
		Node resourceBundleNode = usecaseNode.selectSingleNode("/usecases/usecase/resourceBundle");
		if(null != resourceBundleNode){
			XStream xstream = new XStream(new DomDriver());
			xstream.alias("resourceBundle", UsecaseResourceBundle.class);
			xstream.aliasAttribute(UsecaseResourceBundle.class,"fileName", "fileName");
			xstream.aliasAttribute(UsecaseResourceBundle.class,"fileLocation", "fileLocation");
			UsecaseResourceBundle usecaseBundle = (UsecaseResourceBundle) xstream.fromXML(resourceBundleNode.asXML());
			return usecaseBundle;
		}else
			return null;
	}
	
	/**
	 * 
	 * @param usecaseNode
	 * @return
	 */
	private Actions processAction(Node usecaseNode){
		Node actions = usecaseNode.selectSingleNode("/usecases/usecase/actions");
		if(null != actions){
			XStream xstream = new XStream(new DomDriver());
			xstream.alias("actions", Actions.class);
			xstream.aliasField("action", Actions.class, "actionList");
			xstream.addImplicitCollection(Actions.class, "actionList", Action.class);
			xstream.aliasAttribute(Action.class,"actionType", "actionType");
			xstream.aliasAttribute(Action.class,"actionPerformer", "actionPerformer");
			xstream.aliasAttribute(Action.class,"performerType", "performerType");
			xstream.aliasAttribute(Action.class,"performerPathID", "performerPathID");
			Actions usecaseActions=(Actions) xstream.fromXML(actions.asXML());
			return usecaseActions;
		}else {
			return null;
		}
		
	}

	/**
	 * 
	 * @param usecaseNode
	 * @return
	 */
	private DisplayingPolicies processDisplayPolicies(Node usecaseNode) {
		Node displayPolicies = usecaseNode.selectSingleNode("/usecases/usecase/displayPolicies");
		
		XStream xstream = new XStream(new DomDriver());
		xstream.alias("displayPolicies", DisplayingPolicies.class);
		xstream.aliasAttribute(DisplayingPolicies.class, "displayType", "displayType");
//		xstream.alias("displayType", DisplayingPolicies.class);
		xstream.alias("displayPolicy", DisplayingPolicy.class);
//		xstream.aliasField("displayPolicy",DisplayingPolicies.class,"displayingPolicies");
		xstream.addImplicitCollection(DisplayingPolicies.class, "displayingPolicyList", DisplayingPolicy.class);
		xstream.aliasAttribute(DisplayingPolicy.class, "displayName", "displayName");
		xstream.aliasAttribute(DisplayingPolicy.class, "displayType", "displayType");
		xstream.aliasAttribute(DisplayingPolicy.class, "displayPosition", "displayPosition");
//		xstream.alias("displayName", DisplayingPolicy.class);
//		xstream.alias("displayType", DisplayingPolicy.class);
//		xstream.alias("displayPosition", DisplayingPolicy.class);
		xstream.aliasField("domain-ref", DisplayingPolicy.class, "domainRefs");
		xstream.addImplicitCollection(DisplayingPolicy.class, "domainRefs", String.class);
		
		DisplayingPolicies displayingPolicies =(DisplayingPolicies) xstream.fromXML(displayPolicies.asXML());
		
		return displayingPolicies;
	}

	/**
	 * 
	 * @param usecaseNode
	 * @throws Exception
	 */
	private List<Domains> processDomains(Node usecaseNode) throws Exception {
		List<Domains> domainList = new ArrayList<Domains>();
		List<Element> domains = usecaseNode.selectNodes("/usecases/usecase/domains/domain");
		for (Element domain : domains) {
			XStream xstream = new XStream(new DomDriver());
			xstream.alias("domain", Domains.class);
			xstream.aliasAttribute(Domains.class, "name", "name");
			xstream.aliasAttribute(Domains.class, "ref", "ref");
			xstream.aliasAttribute(Domains.class, "title", "title");
			xstream.aliasAttribute(Domains.class, "position", "position");
			
			/* === Adding Groups to the domain for interdomain grouping === */
			xstream.alias("groups", DomainGroups.class);
			xstream.aliasField("groups", Domains.class, "groups");
			xstream.addImplicitCollection(Domains.class, "groups", DomainGroups.class);
			xstream.aliasAttribute(DomainGroups.class, "groupsDiplayOption", "groups-display-type");
			xstream.aliasAttribute(DomainGroups.class, "groupsPosition", "groups-position");
			xstream.aliasAttribute(DomainGroups.class, "groupsName", "groups-name");
			
			xstream.alias("group", DomainGroup.class);
			xstream.aliasField("group", DomainGroups.class, "groupList");
			xstream.addImplicitCollection(DomainGroups.class, "groupList", DomainGroup.class);
			xstream.aliasAttribute(DomainGroup.class, "groupName", "group-name");
			xstream.aliasAttribute(DomainGroup.class, "groupPosition", "group-position");
			xstream.aliasAttribute(DomainGroup.class, "groupDisplayType", "group-display-type");

			xstream.alias("group-properties", DomainProperties.class);
			xstream.aliasField("group-properties", DomainGroup.class, "properties");	
			
//			TODO: have to work on domainProperties
			xstream.alias("domain-property", DomainProperty.class);
			xstream.aliasField("domain-property", DomainProperties.class, "propertyList");
			xstream.addImplicitCollection(DomainProperties.class, "propertyList", DomainProperty.class);
			xstream.aliasAttribute(DomainProperty.class, "propertyName", "property-name");
			
			xstream.alias("properties", Properties.class);
			
			xstream.alias("property-bindings", PropertyBinding.class);
			xstream.aliasField("property-bindings", Domains.class, "propertiesBindings");
			xstream.addImplicitCollection(PropertyBinding.class, "bindings", Binding.class);
			xstream.alias("binding", Binding.class);
			xstream.aliasField("binding", PropertyBinding.class, "bindings");
			xstream.aliasField("parent", Binding.class, "parent");
			xstream.aliasField("child", Binding.class, "child");
			
			
			xstream.alias("row", Row.class);
			xstream.aliasAttribute(Row.class, "rowNumber", "rowNumber");
			xstream.alias("column", Column.class);
			xstream.aliasAttribute(Column.class, "colPosition", "colPosition");
			
			xstream.alias("widget", Widget.class);
			xstream.aliasAttribute(Widget.class, "property", "property");
			xstream.aliasAttribute(Widget.class, "resourceKey", "resourceKey");
			
			
			xstream.addImplicitCollection(Properties.class, "rows", Row.class); 
			xstream.addImplicitCollection(Row.class, "columns", Column.class);

			xstream.alias("node-type", NodeType.class);
			xstream.alias("node-label", NodeLabel.class);
			xstream.aliasAttribute(NodeLabel.class, "label", "label");
			xstream.aliasAttribute(NodeLabel.class, "resourceKey", "resourceKey");
			xstream.aliasAttribute(NodeType.class, "nodeType", "type");
			xstream.aliasAttribute(NodeType.class, "passwordEncryption", "encryption");
			xstream.aliasField("node-label", Widget.class, "nodeLabel");
			xstream.aliasField("node-type", Widget.class, "nodeType");
			
			xstream.alias("radioButton", RadioButton.class);
			xstream.aliasField("radioButton", NodeType.class, "radioButton");
			
			xstream.addImplicitCollection(RadioButton.class, "radios", Radio.class);
			xstream.alias("radio", Radio.class);
			xstream.aliasAttribute(Radio.class, "label", "label");
			xstream.aliasAttribute(Radio.class, "position", "position");
			xstream.aliasAttribute(Radio.class, "choice", "choice");
			
			
			/* For combobox */
			xstream.alias("combobox-display", ComboBox.class);
			xstream.aliasField("combobox-display", NodeType.class, "combobox");
			xstream.aliasField("display-component", ComboBox.class, "comboDisplayComponentList");
			xstream.aliasAttribute(ComboBox.class, "identionType", "idention-style");
			xstream.aliasAttribute(NodeType.class, "targetBean", "target-bean");
			xstream.aliasAttribute(NodeType.class, "targetType", "target-type");
			xstream.aliasAttribute(NodeType.class, "enumType", "enum-type");
			xstream.addImplicitCollection(ComboBox.class,"comboDisplayComponentList", ComboboxDisplayBean.class);
			xstream.alias("display-component", ComboDisplayComponent.class);
			xstream.aliasAttribute(ComboDisplayComponent.class, "comPosition", "comp-position");
			xstream.aliasAttribute(ComboDisplayComponent.class, "componentName", "comb-component");

			/* For table */
			xstream.alias("table", Table.class);
			xstream.aliasField("table", NodeType.class, "table");
			xstream.addImplicitCollection(Table.class, "tableColumns", TableColumn.class);
			xstream.aliasField("table-column", Table.class, "tableColumns");
			
			xstream.aliasAttribute(TableColumn.class, "label", "label");
			xstream.aliasAttribute(TableColumn.class, "position", "position");
			xstream.aliasAttribute(TableColumn.class, "colType", "colType");
			
//			xstream.aliasField("position", TableColumn.class, "position");
//			xstream.aliasField("table-column", Table.class, "tableColumns");
//			xstream.aliasField("table-column", Table.class, "tableColumns");
//			xstream.aliasField("table-column", Table.class, "tableColumns");
	
			xstream.aliasField("node-validation", Widget.class,	"nodeValidation");
			xstream.aliasAttribute(NodeValidation.class, "maximumLength", "max-length");
			xstream.aliasAttribute(NodeValidation.class, "caseType", "case-type");
			xstream.aliasAttribute(NodeValidation.class, "inputType", "input-type");
			xstream.aliasAttribute(NodeValidation.class, "allowNull", "allow-null");
			
			Domains domainObject =(Domains) xstream.fromXML(domain.asXML());
			domainList.add(domainObject);
		}
		return domainList;
	}
}